8章 スマートコントラクトとVyper
Vyperはイーサリアム仮想マシンのために設計された、実験的なコントラクト指向の言語
誤解を招きやすいコードを書けないようにする
8.1 脆弱性とVyper
100万近いスマートコントラクトの多くに脆弱性があるらしい
脆弱性の分類
自滅的なコントラクト
任意のアドレスによって強制終了されてしまう
グリーディーコントラクト
イーサの解放ができない状態に達する可能性がある
プロディガルコントラクト
イーサを任意のアドレスに対して解放してしまう可能性がある
こういう脆弱性は望ましくない(それはそう)
Vyperは安全なコードを簡単に書けように設計されている
8.2 Solidityとの比較
安全性を提供する方法として、Solidityにある機能を意図的に除外している
ここからは除外された機能と、その理由を見ていく
8.2.1 修飾子
onlyByみたいなチェックするためだけのものならいいが、修飾子はそれ以外のこともできるので危ない
よくわからん副作用があったりするかもしれず、呼び出し側がちゃんとチェックするのは困難
8.2.2 クラスの継承
Solidityには↓があるけどVyperにはない
ポリモーフィズム
多重継承
8.2.3 インラインアセンブリ
インラインアセンブリ、安全なコードが書けるように設計するのならそらサポートしないよなと
低レベルなレイヤにアクセスさせない
8.2.4 関数のオーバーロード
異なる引数をとる同じ名前の関数が複数定義されてると混乱するのでサポートしてないらしい
8.2.5 変数の型キャスト
型キャスト
暗黙的な型キャスト
こっちはサポートしてない
明示的な型キャスト
Vyperでもconvert関数で行える
convert関数を呼び出すと内部的にconversion_tableというものを参照することで適切な変換を保証する
Vyperの明示的な型キャストにおいては情報の欠落はない
欠落する場合は例外が発生する
8.2.6 事前条件と事後条件
Vyperは事前条件・事後条件・状態変化を明示的に扱う
Vyperでスマートコントラクトを書く場合は次の3つに注意する
条件
イーサリアムの状態変数の現在の状態/条件は何か?
影響
スマートコントラクトのコードを実行することで状態変数の条件にどう影響を与えるか?
影響を受けるものは何か?受けないものは?
その影響はスマートコントラクトの意図に合致するか?
相互作用
↑の条件と影響を網羅的に検討する
デプロイする前にコードを論理的に検証し、ありうる永続的な結果や帰結を考察する
他のスマートコントラクトとの相互作用も含む実行シナリオを全て検討する
理想的には、これらをちゃんと検討して、コード内で網羅的にドキュメント化すべき
8.3 デコレータ
Vyperでは↓のデコレータが使える
@private
@public
@constant
これがついてる関数は状態変数を変更できない
@payable
これがついた関数だけがバリューを転送できる
Vyperはデコレータのロジックを明示的に実装していて、
@payableと@constantを両方つけたりするとちゃんとコンパイルエラーになる
8.4 関数と変数の順序
Vyperの関数・変数は呼び出しより前に定義しないといけない
※Solidityはそうじゃなくていいらしい
8.5 コンパイル
Vyperにはオンラインエディタとコンパイラがある
繋がらない
コマンドラインでコントラクトをコンパイルすることももちろんできる
$ vyper ~/hello_world.vy
人間が解釈可能なABI(JSON形式)はこんな感じ
$ vyper -f json ~/hello_world.vy
試しにネットで見つけたこういうコードを
code: hello_world.vy
@public
def helloworld() -> bytes25: return "Hello, world"
コンパイルしてみる
code: compile
$ docker run --rm -v pwd:pwd -w pwd ethereum/vyper ./hello_world.vy
Error compiling: hello_world.vy
vyper.exceptions.TypeMismatchException: line 3:11 Trying to return base type string12, output expecting bytes25 2 def helloworld() -> bytes25: ---> 3 return "Hello, world"
------------------^
↑親切にエラーメッセージが出る〜
メッセージ通りに直して
code: hello_world.vy
@public
def helloworld() -> string12: return "Hello, world"
もっかいコンパイル
code: compile
$ docker run --rm -v pwd:pwd -w pwd ethereum/vyper ./hello_world.vy
0x61018756600436101561000d5761017d565b600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a052632f2f4859600051141561017c5734156100ba57600080fd5b600c610140527f48656c6c6f2c20776f726c640000000000000000000000000000000000000000610160526101408051602001806101e0828460006004600a8704601201f161010857600080fd5b50506101e05160206001820306601f82010390506102406101e0516020818352015b826102405110151561013b57610157565b60006102405161020001535b815160010180835281141561012a575b50505060206101c05260406101e0510160206001820306601f82010390506101c0f350005b5b60006000fd5b61000461018703610004600039610004610187036000f3
人間には読めないのでABIにすると
code: ABI
$ docker run --rm -v pwd:pwd -w pwd ethereum/vyper -f json ./hello_world.vy
いい感じ
8.6 コンパイラレベルでのオーバーフローエラーに対する保護
SolidityではSafeMathライブラリや、Mythril(セキュリティ解析ツール)なんかがある
でもそれらの使用を強制されるわけではないので安全でないコードは書けるし実行できる
Vyperには2つのアプローチを組み合わせたオーバーフロー保護機能が組み込まれている
SafeMathと同等のもの(整数演算に関する例外ケースを含む)
値をクランプする機能
定数リテラルが読み込まれたり、関数に渡されたり変数に割り当てられたりするたびに機能する
LLLコンパイラ内に定義されたカスタム関数で実装されていて無効にできない
※VyperコンパイラはEVMバイトコードではなくLLLを吐き出す
8.7 データの読み書き
ストレージ操作はスマートコントラクトに必要な要素
データの保存
読み込み
修正
スマートコントラクトは2つの場所にデータを書き込むことができる
グローバル状態
特定のスマートコントラクトの状態変数は、イーサリアムのグローバルな状態トライ木に記録されている
スマートコントラクトはその特定のコントラクトアドレスに関連するデータを保存・読み取り・修正できる
他のスマートコントラクトに対して読み書きできない
ログ
ログイベントを通じてイーサリアムのチェーンデータに書き込むこともできる
しかし作成したオンチェーンのログイベントを読み込むことはできない
じゃあ何の利点があるのか?
軽量クライアントによってパブリックチェーン上でログが検出され読み込まれること
例えば、マイニングされたブロックのlogsBoolm値はログイベントが存在してることを示す
ログイベントの存在が確立すれば与えられたトランザクションレシートの値を使っていつでもログデータが得られる
8.8 まとめ
Vyperはコントラクト指向のプログラミング言語
柔軟性を犠牲にして「正確さ」に偏った設計
より良いスマートコントラクトを作成し、深刻な脆弱性を回避できる
hr.icon
次章!